// SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
// SPDX-License-Identifier: GPL-3.0-only

package service_impl

import (
	"encoding/base64"
	"fmt"
	"strings"

	"gitcode.com/openseaotter/so_server/storage"
	"github.com/gin-gonic/gin"
)

func initRoute(engine *gin.Engine) {
	group := engine.Group("/v2")

	group.GET("/", ProcessBase)
	group.GET("/:group/:name/tags/list", ProcessListTag)
	group.HEAD("/:group/:name/manifests/:ref", ProcessStatManifest)
	group.GET("/:group/:name/manifests/:ref", ProcessLoadManifest)
	group.PUT("/:group/:name/manifests/:ref", ProcessStoreManifest)
	group.DELETE("/:group/:name/manifests/:ref", ProcessRemoveManifest)
	group.HEAD("/:group/:name/blobs/:digest", ProcessStatBlob)
	group.GET("/:group/:name/blobs/:digest", ProcessDownloadBlob)
	group.DELETE("/:group/:name/blobs/:digest", ProcessRemoveBlob)
	group.POST("/:group/:name/blobs/uploads/", ProcessInitUpload)
	group.GET("/:group/:name/blobs/uploads/:uuid", ProcessStatUpload)
	group.PATCH("/:group/:name/blobs/uploads/:uuid", ProcessUploadBlob)
	group.PUT("/:group/:name/blobs/uploads/:uuid", ProcessDoneUpload)
	group.DELETE("/:group/:name/blobs/uploads/:uuid", ProcessStopUpload)
	group.GET("/_catalog") //TODO

}

func Run(port uint16, exitChan chan<- error) {
	gin.SetMode(gin.ReleaseMode)

	engine := gin.New()
	engine.Use(gin.Recovery(), AuthCheck())
	engine = engine.With()

	initRoute(engine)
	err := engine.Run(fmt.Sprintf("0.0.0.0:%d", port))
	exitChan <- err
}

func RunTls(port uint16, certFilePath, keyFilePath string, exitChan chan<- error) {
	gin.SetMode(gin.ReleaseMode)

	engine := gin.New()
	engine.Use(gin.Recovery(), AuthCheck())
	engine = engine.With()

	initRoute(engine)
	err := engine.RunTLS(fmt.Sprintf("0.0.0.0:%d", port), certFilePath, keyFilePath)
	exitChan <- err
}

func AuthCheck() gin.HandlerFunc {
	return func(c *gin.Context) {
		authorization := c.GetHeader("Authorization")
		if authorization == "" {
			SendUnAuthError(c, "miss Authorization header")
			return
		}
		if !strings.HasPrefix(authorization, "Basic ") {
			SendUnAuthError(c, "only support basic auth")
			return
		}
		nameAndPassValue, err := base64.StdEncoding.DecodeString(authorization[6:])
		if err != nil {
			SendUnAuthError(c, err.Error())
			return
		}
		pos := strings.Index(string(nameAndPassValue), ":")
		if pos == -1 {
			SendUnAuthError(c, "wrong username")
			return
		}
		username := string(nameAndPassValue[:pos])
		password := string(nameAndPassValue[pos+1:])

		secretInfo, err := storage.AuthSecretDao.Get(c.Request.Context(), username)
		if err != nil {
			SendUnAuthError(c, err.Error())
			return
		}
		if password != secretInfo.Password {
			SendUnAuthError(c, "wrong password")
			return
		}
		SetSecretUsername(c, username)
		c.Next()
	}
}
